package service;

import java.util.HashMap;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Calculator {
	private String expression;

	private HashMap operators;

	private Context ctx;

	public static void main(String[] args) {
		Calculator calc = new Calculator();
		// instantiate the context
		Context ctx = new Context();

		String expr = "15*2";
		
		Pattern pat = Pattern.compile("[*|+|-|/]");
		Matcher mat = pat.matcher(expr);
		if(mat.find())
		{
			
			String tok = mat.group();
			String[] vals = expr.split("[*]");
			ctx.assign("a", Integer.valueOf(vals[0]));
			ctx.assign("b", Integer.valueOf(vals[1]));
			expr = expr.replaceFirst(vals[0], "a");
			expr = expr.replaceFirst(vals[1], "b");
		}
		else
			System.out.println("NO MATCH");
		
		// set the expression to evaluate
		System.out.println("EXPR:" +expr);
		
		calc.setExpression(expr);

		// configure the calculator with the
		// Context
		calc.setContext(ctx);

		// Display the result
		System.out.println(" Variable Values: " + "a=" + ctx.getValue("a")
				+ ", b=" + ctx.getValue("b") + ", c=" + ctx.getValue("c")
				+ ", d=" + ctx.getValue("d"));
		System.out.println(" Result = " + calc.evaluate());
	}

	public Calculator() {
		operators = new HashMap();
		operators.put("+", "1");
		operators.put("-", "1");
		operators.put("/", "2");
		operators.put("*", "2");
		operators.put("(", "0");
	}

	public void setContext(Context c) {
		ctx = c;
	}

	public void setExpression(String expr) {
		expression = expr;
	}

	public int evaluate() {
		// infix to Postfix
		String pfExpr = infixToPostFix(expression);

		// build the Binary Tree
		Expression rootNode = buildTree(pfExpr);

		// Evaluate the tree
		return rootNode.evaluate(ctx);
	}

	private NonTerminalExpression getNonTerminalExpression(String operation,
			Expression l, Expression r) {
		if (operation.trim().equals("+")) {
			return new AddExpression(l, r);
		}
		if (operation.trim().equals("-")) {
			return new SubtractExpression(l, r);
		}
		if (operation.trim().equals("*")) {
			return new MultiplyExpression(l, r);
		}

		return null;
	}

	private Expression buildTree(String expr) {
		Stack s = new Stack();

		for (int i = 0; i < expr.length(); i++) {
			String currChar = expr.substring(i, i + 1);

			if (isOperator(currChar) == false) {
				Expression e = new TerminalExpression(currChar);
				s.push(e);
			} else {
				Expression r = (Expression) s.pop();
				Expression l = (Expression) s.pop();
				Expression n = getNonTerminalExpression(currChar, l, r);
				s.push(n);
			}
		}// for
		return (Expression) s.pop();
	}

	private String infixToPostFix(String str) {
		Stack s = new Stack();
		String pfExpr = "";
		String tempStr = "";

		String expr = str.trim();
		for (int i = 0; i < str.length(); i++) {

			String currChar = str.substring(i, i + 1);

			if ((isOperator(currChar) == false) && (!currChar.equals("("))
					&& (!currChar.equals(")"))) {
				pfExpr = pfExpr + currChar;
			}
			if (currChar.equals("(")) {
				s.push(currChar);
			}
			// for ')' pop all stack contents until '('
			if (currChar.equals(")")) {
				tempStr = (String) s.pop();
				while (!tempStr.equals("(")) {
					pfExpr = pfExpr + tempStr;
					tempStr = (String) s.pop();
				}
				tempStr = "";
			}
			// if the current character is an
			// operator
			if (isOperator(currChar)) {
				if (s.isEmpty() == false) {
					tempStr = (String) s.pop();
					String strVal1 = (String) operators.get(tempStr);
					int val1 = new Integer(strVal1).intValue();
					String strVal2 = (String) operators.get(currChar);
					int val2 = new Integer(strVal2).intValue();

					while ((val1 >= val2)) {
						pfExpr = pfExpr + tempStr;
						val1 = -100;
						if (s.isEmpty() == false) {
							tempStr = (String) s.pop();
							strVal1 = (String) operators.get(tempStr);
							val1 = new Integer(strVal1).intValue();

						}
					}
					if ((val1 < val2) && (val1 != -100))
						s.push(tempStr);
				}
				s.push(currChar);
			}// if

		}// for
		while (s.isEmpty() == false) {
			tempStr = (String) s.pop();
			pfExpr = pfExpr + tempStr;
		}
		return pfExpr;
	}

	private boolean isOperator(String str) {
		if ((str.equals("+")) || (str.equals("-")) || (str.equals("*"))
				|| (str.equals("/")))
			return true;
		return false;
	}
} // End of class

class Context {
	private HashMap varList = new HashMap();

	public void assign(String var, int value) {
		varList.put(var, new Integer(value));
	}

	public int getValue(String var) {
		Integer objInt = (Integer) varList.get(var);
		return objInt.intValue();
	}

	public Context() {
		initialize();
	}

	// Values are hardcoded to keep the example simple
	private void initialize() {
		assign("c", 30);
		assign("d", 10);
	}

}

class TerminalExpression implements Expression {
	private String var;

	public TerminalExpression(String v) {
		var = v;
	}

	public int evaluate(Context c) {
		return c.getValue(var);
	}
}

interface Expression {
	public int evaluate(Context c);
}

abstract class NonTerminalExpression implements Expression {
	private Expression leftNode;

	private Expression rightNode;

	public NonTerminalExpression(Expression l, Expression r) {
		setLeftNode(l);
		setRightNode(r);
	}

	public void setLeftNode(Expression node) {
		leftNode = node;
	}

	public void setRightNode(Expression node) {
		rightNode = node;
	}

	public Expression getLeftNode() {
		return leftNode;
	}

	public Expression getRightNode() {
		return rightNode;
	}
}// NonTerminalExpression

class AddExpression extends NonTerminalExpression {
	public int evaluate(Context c) {
		return getLeftNode().evaluate(c) + getRightNode().evaluate(c);
	}

	public AddExpression(Expression l, Expression r) {
		super(l, r);
	}
}// AddExpression

class SubtractExpression extends NonTerminalExpression {
	public int evaluate(Context c) {
		return getLeftNode().evaluate(c) - getRightNode().evaluate(c);
	}

	public SubtractExpression(Expression l, Expression r) {
		super(l, r);
	}
}// SubtractExpression

class MultiplyExpression extends NonTerminalExpression {
	public int evaluate(Context c) {
		return getLeftNode().evaluate(c) * getRightNode().evaluate(c);
	}

	public MultiplyExpression(Expression l, Expression r) {
		super(l, r);
	}

}// MultiplyExpression

// File: Interprepter.properties
/*
 * a=10 b=20 c=30 d=40
 */